﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Blotter.cs" company="">
//   
// </copyright>
// <summary>
//   The blotter.
// </summary>
// --------------------------------------------------------------------------------------------------------------------


using System;
using System.Linq;

namespace Systemathics.FrameWork
{
    [Serializable]
    public sealed class Blotter : EventArgs, ICloneable 
    {
        private readonly Quote[] blotter;

        public Blotter(Guid id)
        {
            Id = id;
            blotter = new[]
                          {
                              new Quote(id), 
                              new Quote(id), 
                              new Quote(id), 
                              new Quote(id), 
                              new Quote(id)
                          };
        }
        public Guid Id { get; private set; }
        public TickInfo TickInfo { get; set; }
        public Quote this[QuoteLevel level]
        {
            get { return blotter[(int)level]; }
        }

        public int BidVolumeUntil(QuoteLevel level)
        {
            int result = 0;
            lock (this)
            {
                for (int i = 0; i <= (int)level; i++)
                    result += blotter[i].BidSize;
            }

            return result;
        }
        public int BidVolumeUntil(Decimal bidPrice, ref int[] exactQuantities)
        {
            lock (this)
            {
                for (var i = (int)QuoteLevel.One; i <= (int)QuoteLevel.Five; i++)
                    if (blotter[i].Bid <= bidPrice)
                        exactQuantities[i] = blotter[i].BidSize;
            }

            return exactQuantities.Sum();
        }
        public int AskVolumeUntil(QuoteLevel level)
        {
            int result = 0;
            lock (this)
            {
                for (int i = 0; i <= (int)level; i++)
                    result += blotter[i].AskSize;
            }

            return result;
        }
        public int AskVolumeUntil(Decimal askPrice, ref int[] exactQuantities)
        {
            lock (this)
            {
                for (var i = (int)QuoteLevel.One; i <= (int)QuoteLevel.Five; i++)
                    if (blotter[i].Ask <= askPrice)
                        exactQuantities[i] = blotter[i].AskSize;
            }

            return exactQuantities.Sum();
        }
        public Decimal WeigthedBidPriceUntil(QuoteLevel level)
        {
            double result = 0;
            double totalSize = 0;
            lock (this)
            {
                for (int i = 0; i <= (int)level; i++)
                {
                    result += (int)(blotter[i].BidSize * blotter[i].Bid);
                    totalSize += blotter[i].BidSize;
                }
            }

            return (decimal) (result / totalSize);
        }
        public Decimal WeigthedBidPriceUntil(int targetQty, ref int[] exactQuantities, ref QuoteLevel ql)
        {
            Decimal result = 0;
            lock (this)
            {
                FillExactBidQuantities(targetQty, ref exactQuantities, ref ql);
                for (var i = (int)QuoteLevel.One; i < (int)QuoteLevel.Five; i++)
                    result += exactQuantities[i] * blotter[i].Bid;
            }

            return result / exactQuantities.Sum();
        }
        public Decimal WeigthedAskPriceUntil(QuoteLevel level)
        {
            Decimal result = 0;
            Decimal totalSize = 0;
            lock (this)
            {
                for (int i = 0; i <= (int)level; i++)
                {
                    result += (int)(blotter[i].AskSize * blotter[i].Ask);
                    totalSize += blotter[i].AskSize;
                }
            }

            return result / totalSize;
        }
        public Decimal WeigthedAskPriceUntil(int targetQty, ref int[] exactQuantities, ref QuoteLevel ql)
        {
            Decimal result = 0;
            lock (this)
            {
                FillExactAskQuantities(targetQty, ref exactQuantities, ref ql);
                for (var i = (int)QuoteLevel.One; i < (int)QuoteLevel.Five; i++)
                    result += exactQuantities[i] * blotter[i].Ask;
            }

            return result / exactQuantities.Sum();
        }
        private void FillExactAskQuantities(int targetQty, ref int[] exactQuantities, ref QuoteLevel ql)
        {
            int Remains = 0;
            exactQuantities[0] = blotter[0].AskSize;
            if (exactQuantities[0] >= targetQty)
            {
                exactQuantities[0] = targetQty;
                ql = QuoteLevel.One;
                return;
            }

            for (var i = (int)QuoteLevel.Two; i <= (int)QuoteLevel.Five; i++)
            {
                Remains = targetQty - exactQuantities.Sum();
                if (Remains > 0)
                {
                    exactQuantities[i] = (Remains > blotter[i].AskSize) ? blotter[i].AskSize : Remains;
                    ql = (QuoteLevel)i;
                }
            }
        }
        private void FillExactBidQuantities(int targetQty, ref int[] exactQuantities, ref QuoteLevel ql)
        {
            int Remains = 0;
            exactQuantities[0] = blotter[0].BidSize;
            if (exactQuantities[0] >= targetQty)
            {
                exactQuantities[0] = targetQty;
                ql = QuoteLevel.One;
                return;
            }

            for (var i = (int)QuoteLevel.Two; i <= (int)QuoteLevel.Five; i++)
            {
                Remains = targetQty - exactQuantities.Sum();
                if (Remains > 0)
                {
                    exactQuantities[i] = (Remains > blotter[i].BidSize) ? blotter[i].BidSize : Remains;
                    ql = (QuoteLevel)i;
                }
            }
        }

        #region ICloneable Members

        public object Clone()
        {
            return MemberwiseClone();
        }

        #endregion
    }
}